package reflection;

import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Khai Loon
 * @version 2017/2/27 0027 上午 11:22
 */
public class DynamicServer {
    private static Map<String, Class> classMap = new HashMap<>();
    private static Map<String, Object> InstanceMap = new HashMap<>();
    private static Map<String, Method> methodMap = new HashMap<>();
    /**
     * 1  方法返回类型  需要优化 转换成 动态
     */
    private static Map<String, Map<String, String>> typeMap = new HashMap<>();

    /**
     * 通过反射+缓存高效的调用某个类里面的某个方法
     */
    private static Object cacheMethod(String className, String methodString, Object[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class[] parameterTypes = new Class[args == null ? 0 : args.length];
        int size = args == null ? 0 : args.length;
        Method method = methodMap.get(className + "_" + methodString + "_" + size);//用于区分重载的方法
        Object Instance = InstanceMap.get(className);
        if (method == null || Instance == null) {
            Class<? extends Object> objClass = classMap.get(className);

            if (objClass == null) {
                objClass = Class.forName(className);
                classMap.put(className, objClass);//缓存class对象
            }

            Method[] methods = objClass.getDeclaredMethods();
            for (Method m : methods) {
                int count = m.getParameterCount();
                if (count == size && m.getName().equals(methodString)) {
                    Type[] types = m.getParameterTypes();
                    for (int i = 0; i < types.length; i++) {
                        if (types[i].getTypeName().equals("java.lang.Long")) {
                            parameterTypes[i] = Long.class;
                            args[i] = Long.parseLong(String.valueOf(args[i]));
                        } else if (types[i].getTypeName().equals("java.lang.Integer")) {
                            parameterTypes[i] = Integer.class;
                            args[i] = Integer.parseInt(String.valueOf(args[i]));
                        } else if (types[i].getTypeName().equals("java.lang.String")) {
                            parameterTypes[i] = String.class;
                            args[i] = String.valueOf(args[i]);
                        } else if (types[i].getTypeName().equals("java.util.Date")) {
                            parameterTypes[i] = Date.class;//没有处理
                        } else {
                            parameterTypes[i] = null;
                        }
                    }
                    break;
                }
            }

            if (Instance == null) {
                Instance = objClass.newInstance();
                InstanceMap.put(className, Instance);//缓存对象的实例
            }

            if (method == null) {
                method = objClass.getDeclaredMethod(methodString, parameterTypes);
                methodMap.put(className + "_" + methodString + "_" + size, method);//缓存Method对象
            }
        }
        //cacheType(className, method);
        return method.invoke(Instance, args);//动态调用某个对象中的public声明的方法
    }

    /**
     * 反射公用入口
     *
     * @param method 方法名
     * @param args   参数
     * @return
     */
    private static String invoke(String className, String method, Object... args) {

        Object object = null;
        try {
            object = DynamicServer.cacheMethod(className, method, args);
        } catch (NoSuchMethodException | ClassNotFoundException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        return object == null ? null : object.toString();
    }


    /**
     * 通过反射+缓存获取指定类里面 的指定方法的返回类型
     */
    private static String cacheType(String className, String method) throws ClassNotFoundException {
        Map<String, String> methodType = typeMap.get(className);

        if (methodType == null) {
            methodType = new HashMap<>();
            Class objClass = Class.forName(className);
            Method[] methods = objClass.getDeclaredMethods();//获取某个类里面的所有的公共的方法
            if (methods.length > 0) {
                for (Method m : methods) {
                    methodType.put(m.getName(), m.getGenericReturnType().getTypeName());//遍历出所有的方法，将方法名和返回类型存在静态的map中（缓存）
                }
            }
            typeMap.put(className, methodType);
        }

        return typeMap.get(className).get(method);
    }

    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(invoke("com.ylxx.fy.Hello", "thisMethod", "2", 8));
    }

    /**
     * 将查询键值对map转换成对应的实体
     */
    public static Object mapToEntity(Class clazz, Map<String, Object> map) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
        Method[] methods = clazz.getDeclaredMethods();
        try {
            Object instance = clazz.newInstance();
            Method.setAccessible(methods, true);
            StringBuffer key = new StringBuffer();
            String fieldName;
            for (Method method : methods) {
                key.delete(0, key.length());
                fieldName = method.getName();
                if (fieldName.substring(0, 3).equals("set")) {
                    for (int i = 0; i < fieldName.substring(3).length(); i++) {
                        char tempChr = fieldName.substring(3).charAt(i);
                        if (tempChr >= 'A' && tempChr <= 'Z') {
                            key.append("_");
                        }
                        key.append(tempChr);
                    }
                    if (map.containsKey(key.substring(1).toLowerCase())) {
                        String type = method.getParameterTypes()[0].getName();
                        if (type.equals("java.lang.Long")) {
                            method.invoke(instance, Long.valueOf(String.valueOf(map.get(key.substring(1).toLowerCase()))));
                        } else if (type.equals("java.lang.Integer")) {
                            method.invoke(instance, Integer.valueOf(String.valueOf(map.get(key.substring(1).toLowerCase()))));
                        } else if (type.equals("java.lang.String")) {
                            method.invoke(instance, String.valueOf(map.get(key.substring(1).toLowerCase())));
                        } else if (type.equals("java.util.Date")) {
                            String dateStr = String.valueOf(map.get(key.substring(1).toLowerCase()));
                            if (StringUtils.isBlank(dateStr)) {
                                dateStr = "0";
                            }
                            method.invoke(instance, new Date(Long.valueOf(dateStr)));
                        }
                    }
                }
            }
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            Method.setAccessible(methods, false);
        }
        return null;
    }

    /**
     * java bean 转换成map
     */
    public <S> Map<String, Object> entityToMap(S bean) {
        if (bean == null)
            return null;

        Map<String, Object> map = Maps.newHashMap();
        Class<? extends Object> objClass = bean.getClass();
        Field[] files = objClass.getDeclaredFields();
        Method method;
        for (Field field : files) {
            String name = field.getName();
            String getName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
            try {
                method = objClass.getMethod(getName);
                if (method != null) {
                    map.put(name, method.invoke(bean));
                }
            } catch (Exception e) {
                e.getMessage();
            }
        }
        return map;
    }
}
